package com.gilight.base.uiapp.order;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gilight.base.admin.UtilServices;
import com.gilight.base.base.RepStatusCode;
import com.gilight.base.base.exception.AppServiceException;
import com.gilight.base.base.exception.ExceptionDefinition;
import com.gilight.base.base.exception.ServiceException;
import com.gilight.base.component.CacheComponent;
import com.gilight.base.component.CacheConst;
import com.gilight.base.component.LockComponent;
import com.gilight.base.component.LockConst;
import com.gilight.base.config.jwt.util.common.StringUtil;
import com.gilight.base.dao.SysZzjgMapper;
import com.gilight.base.dto.CouponUserDTO;
import com.gilight.base.dto.freight.ShipTraceDTO;
import com.gilight.base.dto.goods.GroupShopDTO;
import com.gilight.base.dto.goods.GroupShopSkuDTO;
import com.gilight.base.dto.goods.SkuDTO;
import com.gilight.base.dto.goods.SpuDTO;
import com.gilight.base.dto.order.OrderDTO;
import com.gilight.base.dto.order.OrderRequestDTO;
import com.gilight.base.dto.order.OrderRequestSkuDTO;
import com.gilight.base.dto.uni.UserDTO;
import com.gilight.base.enums.*;
import com.gilight.base.executor.GlobalExecutor;
import com.gilight.base.mapper.BrandMapper;
import com.gilight.base.mapper.OrderMapper;
import com.gilight.base.mapper.OrderSkuMapper;
import com.gilight.base.mapper.SkuMapper;
import com.gilight.base.model.*;
import com.gilight.base.model.goods.SkuDO;
import com.gilight.base.model.order.OrderDO;
import com.gilight.base.model.order.OrderSkuDO;
import com.gilight.base.model.spu.SpuDO;
import com.gilight.base.mq.DelayedMessageQueue;
import com.gilight.base.properties.UnimallOrderProperties;
import com.gilight.base.properties.UnimallWxAppProperties;
import com.gilight.base.service.address.AddressBizService;
import com.gilight.base.service.cart.CartBizService;
import com.gilight.base.service.coupon.CouponBizService;
import com.gilight.base.service.freight.FreightTemplateBizService;
import com.gilight.base.service.groupshop.GroupShopBizService;
import com.gilight.base.service.notify.AdminNotifyBizService;
import com.gilight.base.service.order.OrderBizService;
import com.gilight.base.service.product.ProductBizService;
import com.gilight.base.util.GeneratorUtil;
import com.gilight.base.util.SessionUtil;
import com.github.binarywang.wxpay.bean.request.WxPayDownloadBillRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayBillResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.jeequan.jeepay.JeepayClient;
import com.jeequan.jeepay.exception.JeepayException;
import com.jeequan.jeepay.model.PayOrderCreateReqModel;
import com.jeequan.jeepay.model.PayOrderCreateResModel;
import com.jeequan.jeepay.request.PayOrderCreateRequest;
import com.jeequan.jeepay.response.PayOrderCreateResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by rize on 2019/7/4.
 */
@Service
public class OrderServiceImpl implements OrderService {

    private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private CouponBizService couponBizService;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderSkuMapper orderSkuMapper;

    @Autowired
    private CartBizService cartBizService;

    @Autowired
    private WxPayService wxPayService;

    @Autowired
    private UtilServices utilServices;

    @Autowired
    private LockComponent lockComponent;

    @Autowired
    private AddressBizService addressBizService;

    @Autowired
    private OrderBizService orderBizService;

    @Autowired
    private FreightTemplateBizService freightTemplateBizService;

    @Autowired
    private GroupShopBizService groupShopBizService;

    @Autowired
    private AdminNotifyBizService adminNotifyBizService;

    @Autowired
    private CacheComponent cacheComponent;

    @Autowired
    private ProductBizService productBizService;

    @Autowired
    private DelayedMessageQueue delayedMessageQueue;
    @Value("${com.iotechn.unimall.machine-no}")
    private String MACHINE_NO;

    @Value("${com.iotechn.unimall.env}")
    private String ENV;

    @Autowired
    private UnimallWxAppProperties unimallWxAppProperties;

    @Autowired
    private UnimallOrderProperties unimallOrderProperties;

    @Autowired
    private SysZzjgMapper sysZzjgMapper;

    @Autowired
    private BrandMapper brandMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String takeOrder(OrderRequestDTO orderRequest, String channel, Long userId, Long zzjgId, Integer isBaiHuo, UserDTO userDTO,String ipRegion) throws ServiceException {
        if (lockComponent.tryLock(LockConst.TAKE_ORDER_LOCK + userId, 20)) {
            final List<SkuDTO> skuDTOListOfAll = new ArrayList<>();  //SKUdto 包含了SPU部分信息如是否是活动商品
            // 商品状态异常列表，用于提示前端
            List<Long> statusErrorSkuList = new LinkedList<>();

            //加上乐观锁，防止用户重复提交订单
            List<OrderRequestSkuDTO> skuList = orderRequest.getSkuList();

            // 从缓存读取，用于计算的列表。属于SkuDO的部分属性。
            List<OrderCalcSkuModel> calcSkuList = new ArrayList<>();

            // 库存不足列表，用于提示前端
            List<SkuStockInfoModel> stockErrorSkuList = new LinkedList<>();

            List<Long> skuIds = new ArrayList<>();

            /**
             * 如果不是百货商品我们的SKU是动态生成的，也就是说前台根据标签动态生成，此时SKU是虚拟的并不存在于数据库
             * 目前的做法是把这些动态生成的SKU持久化到数据库
             */
            if(isBaiHuo == 0){ //只有下单是冷饮的才会进行动态保存SKU,并且这个以后都不会再用了应该保存到缓存中
                Map<Long, List<OrderRequestSkuDTO>> goupBySpuid = skuList.stream().collect(Collectors.groupingBy(OrderRequestSkuDTO::getSpuId));
                //校验库存
                this.checkSpuKuCun(goupBySpuid,stockErrorSkuList);
                Date now = new Date();
                skuList.forEach(orderRequestSkuDTO ->{  //对SKU进行保存
                    SkuDO skuDO = new SkuDO();
                    skuDO.setBarCode(orderRequestSkuDTO.getBarCode());
                    skuDO.setTitle(orderRequestSkuDTO.getTitle());
                    skuDO.setSpecification(orderRequestSkuDTO.getSpecification());
                    skuDO.setOriginalPrice(orderRequestSkuDTO.getOriginalPrice());
                    skuDO.setPrice(orderRequestSkuDTO.getPrice());
                    skuDO.setVipPrice(orderRequestSkuDTO.getVipPrice());
                    skuDO.setStock(9999);
                    skuDO.setWeight(1);
                    skuDO.setGmtCreate(now);
                    skuDO.setGmtUpdate(now);
                    skuDO.setIsBaiHuo(0);
                    skuDO.setSpuId(orderRequestSkuDTO.getSpuId());
                    skuMapper.insert(skuDO);
                    //TODO 进行了数据库操作
                    orderRequestSkuDTO.setSkuId(skuDO.getId());//保存后将id赋值给原请求过期时间是1天
                    cacheComponent.putObj(CacheConst.DYNAMIC_SKU_CACHE+skuDO.getId(),skuDO,3600*24*7);
                    /**
                     * 拿到当前商品
                     * 开始构建计算模型
                     */
                    SpuDTO spuDTO = this.getSpuDtoFormCacheOrDB(orderRequestSkuDTO.getSpuId(),statusErrorSkuList);
                    OrderCalcSkuModel orderCalcSpuDTO = new OrderCalcSkuModel();
                    BeanUtils.copyProperties(spuDTO,orderCalcSpuDTO);
                    orderCalcSpuDTO.setSkuId(skuDO.getId());
                    orderCalcSpuDTO.setNum(orderRequestSkuDTO.getNum());
                    calcSkuList.add(orderCalcSpuDTO);
                    //校验库存
                    //this.checkKuCun(isBaiHuo,skuDO.getId(),orderRequestSkuDTO,stockErrorSkuList);
                    skuIds.add(skuDO.getId());

                    //开始构建SKUDTO不见死不救SKUDO SKUDTO中包含了SPU部分信息 也就是说是两个个表的关联查询
                    SkuDTO skuDTO = new SkuDTO();
                    BeanUtils.copyProperties(skuDO,skuDTO);
                    skuDTO.setCategoryId(spuDTO.getCategoryId());
                    skuDTO.setTitle(orderRequestSkuDTO.getTitle());
                    skuDTO.setSpuImg(spuDTO.getImg());
                    skuDTO.setUnit(spuDTO.getUnit());
                    skuDTO.setStatus(spuDTO.getStatus());
                    skuDTO.setFreightTemplateId(spuDTO.getFreightTemplateId());
                    skuDTO.setSpuTitle(spuDTO.getTitle());
                    skuDTOListOfAll.add(skuDTO);
                });
            }
            boolean calcStockFlag = false;
            try {
                //用户会员等级
                Integer userLevel = SessionUtil.getUser().getLevel();
                // 对Sku排序，防止相互拿锁，两边都无法结算的情况。
                orderRequest.getSkuList().sort((o1, o2) -> (int) (o1.getSkuId() - o2.getSkuId()));
                //参数强校验 START
                if (CollectionUtils.isEmpty(skuList) || orderRequest.getTotalPrice() == null) {
                    throw new AppServiceException(ExceptionDefinition.PARAM_CHECK_FAILED);
                }
                if (orderRequest.getTotalPrice() <= 0) {
                    throw new AppServiceException(ExceptionDefinition.ORDER_PRICE_MUST_GT_ZERO);
                }
                AddressDO addressDO= null;
                if(isBaiHuo==1){
                    // 若是卖虚拟物品，不需要收货地址，可以将此行注释掉
                    if (orderRequest.getAddressId() == null) {
                        throw new AppServiceException(ExceptionDefinition.ORDER_ADDRESS_CANNOT_BE_NULL);
                    }
                      addressDO = addressBizService.getAddressById(orderRequest.getAddressId());
                }

//                // 将SkuIds 查取出来
//                List<Long> skuIds = new ArrayList<>();
//                for (OrderRequestSkuDTO orderRequestSkuDTO : skuList) {
//                    Long skuId = orderRequestSkuDTO.getSkuId();
//                    skuIds.add(skuId);
//                    OrderCalcSkuModel orderCalcSpuDTO = cacheComponent.getHashObj(CacheConst.PRT_SPU_HASH_BUCKET, "P" + orderRequestSkuDTO.getSpuId(), OrderCalcSkuModel.class);
//                    if (orderCalcSpuDTO == null) {
//                        // 尝试从DB中读取
//                        SpuDO spuFromDB = productBizService.getProductByIdFromDB(orderRequestSkuDTO.getSpuId());
//                        if (spuFromDB == null || (spuFromDB.getStatus() == SpuStatusType.STOCK.getCode())) {
//                            // 不存在的或下架的商品
//                            statusErrorSkuList.add(skuId);
//                            continue;
//                        } else {
//                            orderCalcSpuDTO = new OrderCalcSkuModel();
//                            BeanUtils.copyProperties(spuFromDB, orderCalcSpuDTO);
//                        }
//
//                    }
//
//
//                    // 将SkuId设置进去
//                    orderCalcSpuDTO.setSkuId(skuId);
//                    orderCalcSpuDTO.setNum(orderRequestSkuDTO.getNum());
//                    calcSkuList.add(orderCalcSpuDTO);
//                }
                calcStockFlag = true;
                // 商品库存不足列表，用于前端提示 使用异常的Attach方法
                if (!CollectionUtils.isEmpty(stockErrorSkuList)) {
                    throw new AppServiceException(ExceptionDefinition.ORDER_SKU_STOCK_NOT_ENOUGH).attach(stockErrorSkuList);
                }

                // 状态错误列表，用于前端提示
                if (!CollectionUtils.isEmpty(statusErrorSkuList)) {
                    throw new AppServiceException(ExceptionDefinition.ORDER_SKU_NOT_EXIST).attach(statusErrorSkuList);
                }

                Date now = new Date();
                // 若库存充足，也没下架，则获取所有sku实体对象
                //从缓存中获取
//                skuDTOListOfAll = productBizService.getSkuListByIds(skuIds);
                // 优惠券实例  //todo 没有优惠卷就不传
                CouponUserDTO couponUserDTO = null;
                if (orderRequest.getCouponId() != null) {
                    couponUserDTO = couponBizService.getCouponUserById(orderRequest.getCouponId(), userId);
                    if (couponUserDTO == null || couponUserDTO.getGmtUsed() != null) {
                        throw new AppServiceException(ExceptionDefinition.ORDER_COUPON_NOT_EXIST);
                    }
                }

                List<OrderCalcSkuModel> orderCalcSkuList = calcSkuList;
                // 进行主键排序
                orderCalcSkuList.sort((o1, o2) -> (int) (o1.getSkuId() - o2.getSkuId()));
                // 查出来的数据是按主键排序的
                List<SkuDTO> skuDTOList = skuDTOListOfAll;
                skuDTOList.sort((o1, o2) -> (int) (o1.getId() - o2.getId()));
                // 商品总价 = 普通商品价格 + 团购商品价格 (暂未使用)
                int totalSkuPrice = 0;
                // 普通商品价格
                int skuPrice = 0;
                int skuOriginalPrice = 0;
                // 团购商品价格映射
                Map<Long, Integer> groupShopPriceMap = new HashMap<>();
                Map<Long, Integer> groupShopOriginalPriceMap = new HashMap<>();
                for (int i = 0; i < skuDTOList.size(); i++) {
                    OrderCalcSkuModel orderCalcSkuDTO = orderCalcSkuList.get(i);
                    SkuDTO skuDTO = skuDTOList.get(i);
                    Integer originalPrice = skuDTO.getOriginalPrice();
                    // FIXME 对于活动价格，需要在skuDTO中覆盖掉之前价格
                    // FIXME 对于参加活动的商品，商品的非活动价格已经无参考价值
                    if ((skuDTO.getActivityType() != null
                            && skuDTO.getActivityType() == SpuActivityType.GROUP_SHOP.getCode() && skuDTO.getGmtActivityStart() != null
                            && skuDTO.getGmtActivityEnd() != null && skuDTO.getGmtActivityStart().getTime() < now.getTime() && skuDTO.getGmtActivityEnd().getTime() > now.getTime())) {
                        // 这个判断，表示该商品与正在进行的团购活动
                        GroupShopDTO groupShopActivity = groupShopBizService.getGroupShopById(skuDTO.getActivityId());
                        if (groupShopActivity == null) {
                            throw new AppServiceException(ExceptionDefinition.ORDER_GROUP_SHOP_ACTIVITY_HAS_OVER);
                        }
                        // 有可能两个SKU是同一个团购活动，所以需要将这两个活动价格合并
                        Integer oldPrice = groupShopPriceMap.get(groupShopActivity.getId());
                        Integer oldOriginalPrice = groupShopOriginalPriceMap.get(groupShopActivity.getId());
                        Map<Long, GroupShopSkuDTO> groupShopSkuMap = groupShopActivity.getGroupShopSkuDTOList().stream().collect(Collectors.toMap(GroupShopSkuDTO::getSkuId, v -> v));
                        // 累加商品总价
                        totalSkuPrice += groupShopSkuMap.get(skuDTO.getId()).getSkuGroupShopPrice() * orderCalcSkuDTO.getNum();
                        // 将价格覆盖掉
                        skuDTO.setPrice(groupShopSkuMap.get(skuDTO.getId()).getSkuGroupShopPrice());
                        skuDTO.setVipPrice(groupShopSkuMap.get(skuDTO.getId()).getSkuGroupShopPrice());
                        if (oldPrice != null) {
                            groupShopPriceMap.put(groupShopActivity.getId(), oldPrice + (groupShopSkuMap.get(skuDTO.getId()).getSkuGroupShopPrice() * orderCalcSkuDTO.getNum()));
                        } else {
                            groupShopPriceMap.put(groupShopActivity.getId(), groupShopSkuMap.get(skuDTO.getId()).getSkuGroupShopPrice() * orderCalcSkuDTO.getNum());
                        }
                        if (oldOriginalPrice != null) {
                            groupShopOriginalPriceMap.put(groupShopActivity.getId(), oldOriginalPrice + (skuDTO.getOriginalPrice() * orderCalcSkuDTO.getNum()));
                        } else {
                            groupShopOriginalPriceMap.put(groupShopActivity.getId(), skuDTO.getOriginalPrice() * orderCalcSkuDTO.getNum());
                        }
                    } else if (userLevel == UserLevelType.VIP.getCode()) {
                        skuOriginalPrice += originalPrice * orderCalcSkuDTO.getNum();
                        skuPrice += skuDTO.getVipPrice() * orderCalcSkuDTO.getNum();
                        totalSkuPrice += skuDTO.getVipPrice() * orderCalcSkuDTO.getNum();
                    } else {
                        skuOriginalPrice += originalPrice * orderCalcSkuDTO.getNum();
                        skuPrice += skuDTO.getPrice() * orderCalcSkuDTO.getNum();
                        totalSkuPrice += skuDTO.getPrice() * orderCalcSkuDTO.getNum();
                    }
                    // 将DB中的价格，重量赋值给计算模型
                    orderCalcSkuDTO.setFreightTemplateId(skuDTO.getFreightTemplateId());
                    orderCalcSkuDTO.setWeight(skuDTO.getWeight());
                    orderCalcSkuDTO.setPrice(skuDTO.getPrice());
                    orderCalcSkuDTO.setVipPrice(skuDTO.getVipPrice());
                }
                // 拆分单序号
                int childIndex = 1;
                // 获取邮费
                FreightCalcModel freightCalcModel = new FreightCalcModel();
                // 将SKU按照不同的运费模板进行分组
                Map<Long, List<OrderCalcSkuModel>> freightTemplateCalcMap = orderCalcSkuList
                        .stream()
                        .collect(Collectors.groupingBy(OrderCalcSkuModel::getFreightTemplateId));
                // 获取SKU数量映射表
                List<FreightCalcModel.FreightAndWeight> faws = new LinkedList<>();
                freightTemplateCalcMap.forEach((k, v) -> {
                    FreightCalcModel.FreightAndWeight faw = new FreightCalcModel.FreightAndWeight();
                    faw.setId(k);
                    int weight = 0;
                    int price = 0;
                    for (OrderCalcSkuModel orderCalcSkuModel : v) {
                        weight += orderCalcSkuModel.getWeight() * orderCalcSkuModel.getNum();
                        price += (userLevel == UserLevelType.VIP.getCode() ? orderCalcSkuModel.getVipPrice() : orderCalcSkuModel.getPrice()) * orderCalcSkuModel.getNum();
                    }
                    faw.setPrice(price);
                    faw.setWeight(weight);
                    faws.add(faw);
                });
                freightCalcModel.setFreightAndWeights(faws);
                Integer freightPrice = freightTemplateBizService.computePostage(freightCalcModel);
                // 是否已经计算过单次费用，例如优惠券、运费。同一个商家只计算一次（因为团购订单可能会被拆为两单，但是邮费只应该计算一次）
                boolean singleFee = false;
                // 使用优惠券的订单
                Long useCouponOrderId = null;
                // 生成一个父单号
                String parentOrderNo = GeneratorUtil.genOrderId(this.MACHINE_NO, this.ENV);
                if (skuPrice > 0) {
                    // 这是普通商品
                    // 将普通商品(非团购等需要单独拆单的商品)的SkuList过滤出来
                    List<SkuDTO> commonSkuList = new ArrayList<>();
                    List<OrderCalcSkuModel> commonOrderCalcSkuList = new ArrayList<>();
                    for (int i = 0; i < skuDTOList.size(); i++) {
                        SkuDTO item = skuDTOList.get(i);
                        if (!(item.getActivityType() != null
                                && item.getActivityType() == SpuActivityType.GROUP_SHOP.getCode() && item.getGmtActivityStart() != null
                                && item.getGmtActivityEnd() != null && item.getGmtActivityStart().getTime() < now.getTime() && item.getGmtActivityEnd().getTime() > now.getTime())) {
                            commonSkuList.add(item);
                            commonOrderCalcSkuList.add(orderCalcSkuList.get(i));
                        }
                    }
                    // 保存订单
                    OrderDO o = save(skuOriginalPrice, skuPrice, channel, freightPrice,
                            couponUserDTO, orderRequest, parentOrderNo, childIndex,
                            userId, now, addressDO, commonSkuList, commonOrderCalcSkuList, userLevel, null, null,zzjgId,ipRegion);
                    useCouponOrderId = o.getId();
                    // 标记已经计算过运费和优惠券了
                    singleFee = true;
                }
                if (groupShopPriceMap.size() > 0) {
                    // 存在团购商品，分别分单处理团购商品
                    Set<Long> groupShopIds = groupShopPriceMap.keySet();
                    for (Long groupShopId : groupShopIds) {
                        Integer groupShopSkuOriginalPrice = groupShopOriginalPriceMap.get(groupShopId);
                        Integer groupShopSkuPrice = groupShopPriceMap.get(groupShopId);
                        // 过滤出当前团购的团购商品，即SkuDTOList
                        List<SkuDTO> groupShopSkuList = new ArrayList<>();
                        List<OrderCalcSkuModel> groupShopOrderCalcSkuList = new ArrayList<>();
                        for (int i = 0; i < skuDTOList.size(); i++) {
                            SkuDTO item = skuDTOList.get(i);
                            if (item.getActivityType() != null
                                    && item.getActivityType() == SpuActivityType.GROUP_SHOP.getCode() && item.getGmtActivityStart() != null && item.getActivityId().longValue() == groupShopId.longValue()
                                    && item.getGmtActivityEnd() != null && item.getGmtActivityStart().getTime() < now.getTime() && item.getGmtActivityEnd().getTime() > now.getTime()) {
                                groupShopSkuList.add(item);
                                groupShopOrderCalcSkuList.add(calcSkuList.get(i));
                            }
                        }
                        if (singleFee) {
                            // 保存子单前，将子单序列累加
                            childIndex++;
                            save(groupShopSkuOriginalPrice, groupShopSkuPrice, channel, 0,
                                    couponUserDTO, orderRequest, parentOrderNo, childIndex,
                                    userId, now, addressDO, groupShopSkuList
                                    , groupShopOrderCalcSkuList, userLevel
                                    , SpuActivityType.GROUP_SHOP.getCode()
                                    , groupShopId
                                    ,zzjgId,ipRegion);
                        } else {
                            // 保存子单前，将子单序列累加
                            childIndex++;
                            save(groupShopSkuOriginalPrice, groupShopSkuPrice, channel, freightPrice,
                                    couponUserDTO, orderRequest, parentOrderNo, childIndex,
                                    userId, now, addressDO, groupShopSkuList
                                    , groupShopOrderCalcSkuList
                                    , userLevel, SpuActivityType.GROUP_SHOP.getCode()
                                    , groupShopId
                                    , zzjgId,ipRegion);
                            // 只收一次运费
                            singleFee = true;
                        }
                    }
                }
                Map<Long, Integer> skuStockMap = skuList.stream().collect(Collectors.toMap(OrderRequestSkuDTO::getSkuId, OrderRequestSkuDTO::getNum));
                // 减少商品库存，microFix。使用事务通知
                productBizService.decSkuStock(skuStockMap);
                // 扣除优惠券，microFix。使用事务通知
                if (couponUserDTO != null) {
                    couponBizService.useCoupon(orderRequest.getCouponId(), useCouponOrderId);
                }
                // 若购物车结算，删除这些购物车的商品
                if (orderRequest.getTakeWay().equals("cart")) {
                    cartBizService.deleteBySkuId(skuIds, userId);
                }
                // 与前端预览的金额比对，若因为时间差不一致，则告诉用户
                if (orderRequest.getExceptPrice() != null) {
                    int exceptPrice = this.checkPrepay(parentOrderNo, null, userId);
                    if (exceptPrice != orderRequest.getExceptPrice().intValue()) {
                        throw new AppServiceException(ExceptionDefinition.ORDER_PRODUCT_PRICE_HAS_BEEN_CHANGED);
                    }
                }
                // 日志 & 发送一个订单自动取消定时任务
                for (int i = 1; i <= childIndex; i++) {
                    String childOrderNo = parentOrderNo + "S" + ((1000) + i);
                    logger.info("订单创建成功:" + childOrderNo);
                    delayedMessageQueue.publishTask(DMQHandlerType.ORDER_AUTO_CANCEL.getCode(), childOrderNo, unimallOrderProperties.getAutoCancelTime().intValue());
                }

                return parentOrderNo;
            } catch (Exception e) {
                if (calcStockFlag) {
                    for (OrderRequestSkuDTO orderRequestSkuDTO : skuList) {
                        cacheComponent.incrementHashKey(CacheConst.PRT_SPU_STOCK_BUCKET, "C" + orderRequestSkuDTO.getSpuId(), orderRequestSkuDTO.getNum());
                        cacheComponent.incrementHashKey(CacheConst.PRT_SKU_STOCK_BUCKET, "K" + orderRequestSkuDTO.getSkuId(), orderRequestSkuDTO.getNum());
                    }
                }
                if (e instanceof ServiceException) {
                    // 服务异常
                    throw e;
                }
                // 未知异常
                logger.error("[提交订单] 异常", e);
                throw e;
            } finally {
                lockComponent.release(LockConst.TAKE_ORDER_LOCK + userId);
            }
        }
        throw new AppServiceException(ExceptionDefinition.ORDER_SYSTEM_BUSY);
    }

    /**
     * 校验库存是否正确
     * @param isBaiHuo
     * @param skuId
     * @param orderRequestDTO
     * @param stockErrorSkuList
     */
    private void checkKuCun(Integer isBaiHuo,Long skuId,OrderRequestSkuDTO orderRequestDTO,List<SkuStockInfoModel> stockErrorSkuList ){

        if(isBaiHuo != 0){ //在不是百货的情况下才会校验库存
            long surplus = cacheComponent.decrementHashKey(CacheConst.PRT_SKU_STOCK_BUCKET, "K" + skuId, orderRequestDTO.getNum());
            // 收货地址对象
            if (surplus < 0) {
                // 若余量小于0，则表示该商品不存在或库存不足。
                SkuStockInfoModel skuStockInfo = new SkuStockInfoModel();
                skuStockInfo.setSkuId(skuId);
                skuStockInfo.setExpect(orderRequestDTO.getNum());
                // 扣减之后的余量 + 用户期望量 = 扣减之前的余量
                skuStockInfo.setSurplus((int) surplus + orderRequestDTO.getNum());
                stockErrorSkuList.add(skuStockInfo);
            }
        }else{

            long surplus = cacheComponent.decrementHashKey(CacheConst.PRT_SPU_STOCK_BUCKET, "C" + orderRequestDTO.getSpuId(), orderRequestDTO.getNum());
            if (surplus < 0) {
                // 若余量小于0，则表示该商品不存在或库存不足。
                SkuStockInfoModel skuStockInfo = new SkuStockInfoModel();
                skuStockInfo.setSkuId(orderRequestDTO.getSpuId());
                skuStockInfo.setExpect(orderRequestDTO.getNum());
                // 扣减之后的余量 + 用户期望量 = 扣减之前的余量
                skuStockInfo.setSurplus((int) surplus + orderRequestDTO.getNum());
                stockErrorSkuList.add(skuStockInfo);
            }
        }
    }
    private void checkSpuKuCun(Map<Long, List<OrderRequestSkuDTO>> goupBySpuid,List<SkuStockInfoModel> stockErrorSkuList){
        goupBySpuid.forEach((k,v)->{
            int sum = 0;
            String title = "";
            for(int i= 0;i<v.size();i++){
                OrderRequestSkuDTO orderRequestSkuDTO = v.get(i);
                title = orderRequestSkuDTO.getTitle();
                sum+=orderRequestSkuDTO.getNum();
            }
            if(cacheComponent.getHashObj(CacheConst.PRT_SPU_STOCK_BUCKET, "C" + k,Object.class)==null){
                return;
            }
            long surplus = cacheComponent.decrementHashKey(CacheConst.PRT_SPU_STOCK_BUCKET, "C" + k, sum);
            if (surplus < 0) {
                // 若余量小于0，则表示该商品不存在或库存不足。
                SkuStockInfoModel skuStockInfo = new SkuStockInfoModel();
                skuStockInfo.setSkuId(k);
                skuStockInfo.setExpect(sum);
                skuStockInfo.setSpuTitle(title);
                // 扣减之后的余量 + 用户期望量 = 扣减之前的余量
                skuStockInfo.setSurplus((int) surplus + sum);
                stockErrorSkuList.add(skuStockInfo);
            }

        });

    }
    private SpuDTO getSpuDtoFormCacheOrDB(Long spuId,List<Long> statusErrorSkuList){
        SpuDTO spuDTO = cacheComponent.getHashObj(CacheConst.PRT_SPU_HASH_BUCKET, "P" + spuId, SpuDTO.class);
        if (spuDTO == null) {
            // 尝试从DB中读取
            SpuDO spuFromDB = productBizService.getProductByIdFromDB(spuId);
            if (spuFromDB == null || (spuFromDB.getStatus() == SpuStatusType.STOCK.getCode())) {
                // 不存在的或下架的商品
                statusErrorSkuList.add(spuId);
            }
            else {
                spuDTO = new SpuDTO();
                BeanUtils.copyProperties(spuFromDB, spuDTO);
            }

        }
        return spuDTO;
    }
    @Override
    public Page<OrderDTO> getOrderPage(Integer pageNo, Integer pageSize, Integer status, Long userId,List<Long> zzjgIds) throws ServiceException {
        List<OrderDTO> orderDTOList = orderMapper.selectOrderPage(status, (pageNo - 1) * pageSize, pageSize, userId,zzjgIds);
        Long count = orderMapper.countOrder(status, (pageNo - 1) * pageSize, pageSize, userId,zzjgIds);
        //封装SKU
        orderDTOList.forEach(item -> {
            item.setSkuList(orderSkuMapper.selectList(new QueryWrapper<OrderSkuDO>().eq("order_id", item.getId())));
        });
        return new Page<>(orderDTOList, pageNo, pageSize, count);
    }

    /**
     * 该方法区别上一个方法是status 上一个status是一个状态  该方法是一个数组
     * @param pageNo
     * @param pageSize
     * @param status
     * @param userId
     * @return
     * @throws ServiceException
     */
    @Override
    public Page<OrderDTO> getOrderPageStatus(Integer pageNo, Integer pageSize, List<Integer>status, Long userId,List<Long> zzjgIds) throws ServiceException {
        List<OrderDTO> orderDTOList = orderMapper.selectOrderPageByStatus(status, (pageNo - 1) * pageSize, pageSize, userId,zzjgIds);
        Long count = orderMapper.countOrderByStatus(status, (pageNo - 1) * pageSize, pageSize, userId,zzjgIds);
        //封装SKU
        orderDTOList.forEach(item -> {
            item.setSkuList(orderSkuMapper.selectList(new QueryWrapper<OrderSkuDO>().eq("order_id", item.getId())));
        });
        return new Page<>(orderDTOList, pageNo, pageSize, count);
    }
    @Override
    public OrderDTO getOrderDetail(Long orderId, Long userId, String orderNo) throws ServiceException {
        return orderBizService.getOrderDetail(orderId, userId,orderNo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object wxPrepay(String parentOrderNo, String orderNo, String ip, Long userId, Long zzjgId, UserDTO userDTO) throws ServiceException {
        int actualPrice = this.checkPrepay(parentOrderNo, orderNo, userId);

        Integer loginType = userDTO.getLoginType();
        String appId;
        String tradeType;
        if (UserLoginType.MP_WEIXIN.getCode() == loginType) {
            appId = unimallWxAppProperties.getMiniAppId();
            tradeType = WxPayConstants.TradeType.JSAPI;
        } else if (UserLoginType.APP_WEIXIN.getCode() == loginType || UserLoginType.REGISTER.getCode() == loginType) {
            appId = unimallWxAppProperties.getAppId();
            tradeType = WxPayConstants.TradeType.APP;
        } else if (UserLoginType.H5_WEIXIN.getCode() == loginType) {
            appId = unimallWxAppProperties.getH5AppId();
            tradeType = WxPayConstants.TradeType.JSAPI;
        } else {
            throw new AppServiceException(ExceptionDefinition.ORDER_LOGIN_TYPE_NOT_SUPPORT_WXPAY);
        }
        SysZzjg sysZzjg = sysZzjgMapper.selectByPrimaryKey(zzjgId);
        //int actualPrice = 1;
//        UserDTO userDTO = new UserDTO();
////        userDTO.setLoginType(UserLoginType.MP_WEIXIN.getCode());
////        userDTO.setOpenId("oq5Dd5SY1AGO7t7d-9ZyqOrtS-f0");
////        SessionUtil.setUser(userDTO);
        Long brandId = sysZzjg.getBrandId();
        if(brandId!=null){
            BrandDo brandDo = brandMapper.selectOne(new QueryWrapper<BrandDo>().eq("id",brandId));
            appId = brandDo.getMiniAppId();
        }
        try {
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            //            // 设置微信请求基本信息
            orderRequest.setAppid(appId);
            //orderRequest.setAppid("wxbe70b7267c6b596f");
            //orderRequest.setSubAppId("wx2249afc93d5d4267");
            // 区分回调 直接通过 S 来判断
            orderRequest.setOutTradeNo(StringUtils.isEmpty(parentOrderNo) ? orderNo : parentOrderNo);
//            orderRequest.setOpenid(SessionUtil.getUser().getOpenId());
            orderRequest.setOpenid(userDTO.getOpenId());
            //orderRequest.setSubOpenid(userDTO.getOpenId());
            orderRequest.setBody("buy_" + (StringUtils.isEmpty(parentOrderNo) ? orderNo : parentOrderNo));
            orderRequest.setTotalFee(actualPrice);
            orderRequest.setSpbillCreateIp(ip);
            orderRequest.setTradeType(tradeType);

            orderRequest.setSubMchId(sysZzjg.getMchId());

            if(StringUtil.isNotBlank(sysZzjg.getTaichuMchId())&& sysZzjg.getTaichuMchAppId()!=null && sysZzjg.getTaichuMchApikey()!=null){ //说明该商户使用Taichu订单系统进行下单
                return this.taichuCreateOrder(orderRequest,sysZzjg);
            }

            /**
             * 还有一种可能 该服务商关联的主题小程序是其它小程序时做一些操作
             */
            utilServices.doSpecISP(sysZzjg,orderRequest);


            WxPayService otherWxPayService =  utilServices.getOtherISPService(sysZzjg);
            if(otherWxPayService !=null){
                return otherWxPayService.createOrder(orderRequest);
            }
            return wxPayService.createOrder(orderRequest);
        } catch (WxPayException e) {
            logger.error("[微信支付] 异常", e);
            throw new AppServiceException(e.getErrCodeDes(), ExceptionDefinition.THIRD_PART_SERVICE_EXCEPTION.getCode(), RepStatusCode.BUSINESS_ERROR);
        } catch (Exception e) {
            logger.error("[预付款异常]", e);
            throw new AppServiceException(ExceptionDefinition.ORDER_UNKNOWN_EXCEPTION);
        }
    }

    /**
     * 该订单会被太初系统进行下单
     * @param orderRequest
     * @param sysZzjg
     * @return
     */
    private Object taichuCreateOrder(WxPayUnifiedOrderRequest orderRequest, SysZzjg sysZzjg) {
        String muchId = sysZzjg.getTaichuMchId();
        String muchAppid = sysZzjg.getTaichuMchAppId();
        String muchAppApiKey =  sysZzjg.getTaichuMchApikey();
        String mchOrderNo = orderRequest.getOutTradeNo();
        String openID = orderRequest.getOpenid();
        Integer amount = orderRequest.getTotalFee();
        String ip = orderRequest.getSpbillCreateIp();
        String body = orderRequest.getBody();
        String wayCode = "WX_LITE";
        String payDataType = "wxapp";
        Byte divisionMode = 2;

        PayOrderCreateRequest request = new PayOrderCreateRequest();
        PayOrderCreateReqModel model = new PayOrderCreateReqModel();
        request.setBizModel(model);

        model.setMchNo(muchId); // 商户号
        model.setAppId(muchAppid);
        model.setMchOrderNo(mchOrderNo);
        model.setWayCode(wayCode);
        model.setAmount(Integer.toUnsignedLong(amount));
        model.setCurrency("CNY");
        model.setClientIp(ip);
        model.setSubject(body);
        model.setBody(body);
        model.setNotifyUrl("http://t44up7g2znmu.ngrok2.xiaomiqiu.cn/api/anon/paytestNotify/payOrder"); //回调地址
        //model.setNotifyUrl("http://t44up7g2znmu.ngrok2.xiaomiqiu.cn/cb/notify/wxpay"); //回调地址
        model.setDivisionMode(divisionMode); //分账模式
        model.setExtParam("sub");
        //设置扩展参数
        JSONObject extParams = new JSONObject();
        if(org.apache.commons.lang3.StringUtils.isNotEmpty(payDataType)) {
            extParams.put("payDataType", payDataType.trim());
        }
        //oPE1O4wxw1rNuEQTZ1-v0y0nx-3g
        extParams.put("openid",openID);
        model.setChannelExtra(extParams.toString());
        JeepayClient jeepayClient = new JeepayClient("http://localhost:9216", muchAppApiKey);

        try {
            PayOrderCreateResponse response = jeepayClient.execute(request);
            if(response.getCode() != 0){
                System.out.println(response.getMsg());
            }
            PayOrderCreateResModel payOrderCreateReqModel = response.get();
            String jsonPayData = payOrderCreateReqModel.getPayData();
            JSONObject jsonObject = JSONObject.parseObject(jsonPayData);
            jsonObject.put("packageValue",jsonObject.get("package"));
//            jsonObject.put("signType","RSA");
            return jsonObject;
        } catch (JeepayException e) {
            e.printStackTrace();
        }

        return null;


    }

    private int checkPrepay(String parentOrderNo, String orderNo, Long userId) throws ServiceException {
        // 两个都为空 和 两个都不为空是不合法的
        if ((StringUtils.isEmpty(parentOrderNo) && StringUtils.isEmpty(orderNo)) || (!StringUtils.isEmpty(parentOrderNo) && !StringUtils.isEmpty(orderNo))) {
            throw new AppServiceException(ExceptionDefinition.ORDER_PARAM_CHECK_FAILED);
        }
        List<OrderDO> orderList;
        if (!StringUtils.isEmpty(parentOrderNo))
            orderList = orderBizService.checkOrderExistByParentNo(parentOrderNo, userId);
        else
            orderList = orderBizService.checkOrderExistByNo(orderNo, userId);
        // 检测订单状态
        int actualPrice = 0;
        for (OrderDO orderDO : orderList) {
            Integer status = orderDO.getStatus();
            if (status != OrderStatusType.UNPAY.getCode()) {
                throw new AppServiceException(ExceptionDefinition.ORDER_STATUS_NOT_SUPPORT_PAY);
            }
            actualPrice += orderDO.getActualPrice();
        }
        return actualPrice;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object offlinePrepay(String parentOrderNo, String orderNo, Long userId) throws ServiceException {
        // 两个都为空 和 两个都不为空是不合法的
        if ((StringUtils.isEmpty(parentOrderNo) && StringUtils.isEmpty(orderNo)) || (!StringUtils.isEmpty(parentOrderNo) && !StringUtils.isEmpty(orderNo))) {
            throw new AppServiceException(ExceptionDefinition.ORDER_PARAM_CHECK_FAILED);
        }
        List<OrderDO> orderList;
        if (!StringUtils.isEmpty(parentOrderNo))
            orderList = orderBizService.checkOrderExistByParentNo(parentOrderNo, userId);
        else
            orderList = orderBizService.checkOrderExistByNo(orderNo, userId);
        // 检测订单状态
        for (OrderDO orderDO : orderList) {
            Integer status = orderDO.getStatus();
            if (status != OrderStatusType.UNPAY.getCode()) {
                throw new AppServiceException(ExceptionDefinition.ORDER_STATUS_NOT_SUPPORT_PAY);
            }
        }
        Date now = new Date();
        for (OrderDO orderDO : orderList) {
            List<OrderSkuDO> orderSkuDOList = orderSkuMapper.selectList(new QueryWrapper<OrderSkuDO>().eq("order_id", orderDO.getId()));
            List<OrderSkuDO> groupShopSkuList = orderSkuDOList.stream().filter(item -> (item.getActivityType() != null && item.getActivityType() == SpuActivityType.GROUP_SHOP.getCode())).collect(Collectors.toList());
            if (groupShopSkuList.size() > 0) {
                // 订单中是否是团购商品
                OrderDO groupShopUpdateDO = new OrderDO();
                groupShopUpdateDO.setPayId("OFFLINE");
                groupShopUpdateDO.setPayChannel(PayChannelType.OFFLINE.getCode());
                groupShopUpdateDO.setPayPrice(orderDO.getActualPrice());
                groupShopUpdateDO.setGmtPay(now);
                groupShopUpdateDO.setGmtUpdate(now);
                groupShopUpdateDO.setStatus(OrderStatusType.GROUP_SHOP_WAIT.getCode());
                groupShopUpdateDO.setSubPay(1);
                // 增加buyer count
                for (OrderSkuDO orderSkuDO : groupShopSkuList) {
                    groupShopBizService.incGroupShopNum(orderSkuDO.getActivityId(), orderSkuDO.getNum());
                }
                orderBizService.changeOrderSubStatus(orderDO.getOrderNo(), OrderStatusType.UNPAY.getCode(), groupShopUpdateDO);
            } else {
                OrderDO updateOrderDO = new OrderDO();
                updateOrderDO.setPayChannel(PayChannelType.OFFLINE.getCode());
                updateOrderDO.setStatus(OrderStatusType.WAIT_STOCK.getCode());
                updateOrderDO.setGmtUpdate(new Date());
                boolean succ = orderBizService.changeOrderSubStatus(orderDO.getOrderNo(), OrderStatusType.UNPAY.getCode(), updateOrderDO);
                if (!succ) {
                    throw new AppServiceException(ExceptionDefinition.ORDER_STATUS_CHANGE_FAILED);
                }
            }
            // 增加商品销量
            Map<Long, Integer> salesMap = orderSkuDOList.stream().collect(Collectors.toMap(OrderSkuDO::getSpuId, OrderSkuDO::getNum, (k1, k2) -> k1.intValue() + k2.intValue()));
            productBizService.incSpuSales(salesMap);
        }
        // 删除自动取消订单消息
        delayedMessageQueue.deleteTask(DMQHandlerType.ORDER_AUTO_CANCEL.getCode(), orderNo);
        return "ok";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String refund(String orderNo, String reason, Long userId) throws ServiceException {
        OrderDO orderDO = orderBizService.checkOrderExistByNo(orderNo, userId).get(0);
        if (PayChannelType.OFFLINE.getCode().equals(orderDO.getPayChannel())) {
            throw new AppServiceException(ExceptionDefinition.ORDER_PAY_CHANNEL_NOT_SUPPORT_REFUND);
        }
        if (OrderStatusType.refundable(orderDO.getStatus())) {
            OrderDO updateOrderDO = new OrderDO();
            updateOrderDO.setRefundReason(reason);
            updateOrderDO.setStatus(OrderStatusType.REFUNDING.getCode());
            orderBizService.changeOrderSubStatus(orderNo, orderDO.getStatus(), updateOrderDO);
            GlobalExecutor.execute(() -> {
                OrderDTO orderDTO = new OrderDTO();
                BeanUtils.copyProperties(orderDO, orderDTO);
                List<OrderSkuDO> orderSkuList = orderSkuMapper.selectList(new QueryWrapper<OrderSkuDO>().eq("order_no", orderDO.getOrderNo()));
                orderDTO.setSkuList(orderSkuList);
                adminNotifyBizService.refundOrder(orderDTO);
            });
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.ORDER_STATUS_NOT_SUPPORT_REFUND);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String cancel(String orderNo, Long userId) throws ServiceException {
        OrderDO orderDO = orderBizService.checkOrderExistByNo(orderNo, userId).get(0);
        if (orderDO.getStatus() != OrderStatusType.UNPAY.getCode()) {
            throw new AppServiceException(ExceptionDefinition.ORDER_STATUS_NOT_SUPPORT_CANCEL);
        }
        OrderDO updateOrderDO = new OrderDO();
        updateOrderDO.setStatus(OrderStatusType.CANCELED.getCode());
        updateOrderDO.setGmtUpdate(new Date());
        List<OrderSkuDO> orderSkuList = orderSkuMapper.selectList(new QueryWrapper<OrderSkuDO>().eq("order_id", orderDO.getId()));
        orderSkuList.forEach(item -> {
            skuMapper.returnSkuStock(item.getSkuId(), item.getNum());
        });
        orderBizService.changeOrderSubStatus(orderNo, OrderStatusType.UNPAY.getCode(), updateOrderDO);
        delayedMessageQueue.deleteTask(DMQHandlerType.ORDER_AUTO_CANCEL.getCode(), orderNo);
        Long couponId = orderDO.getCouponId();
        if(couponId !=null){//如果该订单使用了优惠卷，那么应该把优惠卷返回给用户
            orderBizService.backCouponToUser(couponId,userId,orderDO.getId());
        }
        return "ok";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String confirm(String orderNo, Long userId) throws ServiceException {
        OrderDO orderDO = orderBizService.checkOrderExistByNo(orderNo, userId).get(0);
        if (orderDO.getStatus() != OrderStatusType.WAIT_CONFIRM.getCode()) {
            throw new AppServiceException(ExceptionDefinition.ORDER_STATUS_NOT_SUPPORT_CONFIRM);
        }
        OrderDO updateOrderDO = new OrderDO();
        updateOrderDO.setStatus(OrderStatusType.WAIT_APPRAISE.getCode());
        updateOrderDO.setGmtUpdate(new Date());
        orderBizService.changeOrderSubStatus(orderNo, OrderStatusType.WAIT_CONFIRM.getCode(), updateOrderDO);
        delayedMessageQueue.deleteTask(DMQHandlerType.ORDER_AUTO_CONFIRM.getCode(), orderNo);
        return "ok";
    }

    @Override
    public ShipTraceDTO queryShip(String orderNo, Long userId) throws ServiceException {
        OrderDO orderDO = orderBizService.checkOrderExistByNo(orderNo, userId).get(0);
        if (orderDO.getStatus() < OrderStatusType.WAIT_CONFIRM.getCode()) {
            throw new AppServiceException(ExceptionDefinition.ORDER_HAS_NOT_SHIP);
        }
        if (StringUtils.isEmpty(orderDO.getShipCode()) || StringUtils.isEmpty(orderDO.getShipNo())) {
            throw new AppServiceException(ExceptionDefinition.ORDER_DID_NOT_SET_SHIP);
        }
        ShipTraceDTO shipTraceList = freightTemplateBizService.getShipTraceList(orderDO.getShipNo(), orderDO.getShipCode());
        if (CollectionUtils.isEmpty(shipTraceList.getTraces())) {
            throw new AppServiceException(ExceptionDefinition.ORDER_DO_NOT_EXIST_SHIP_TRACE);
        }
        return shipTraceList;
    }

    @Override
    public Integer previewFreight(OrderRequestDTO orderRequest, Long userId) throws ServiceException {
        List<OrderRequestSkuDTO> skuList = orderRequest.getSkuList();
        AddressDO addressDO = null;
        if (orderRequest.getAddressId() != null) {
            addressDO = addressBizService.getAddressById(orderRequest.getAddressId());
        }
        FreightCalcModel calcModel = new FreightCalcModel();
        if (addressDO == null) {
            // 若没穿省份，则传一个不存在的省份。系统会默认他是全国。
            calcModel.setProvince("一个不存在的地址");
        } else {
            calcModel.setProvince(addressDO.getProvince());
        }
        // 由于是预览，此处可详细用户从前端传入进来的 商品运费模板Id 重量 价格等信息
        UserDTO user = SessionUtil.getUser();
        // 将SKU按照运费模板分组
        Map<Long, List<OrderRequestSkuDTO>> calcMap = skuList.stream().collect(Collectors.groupingBy(OrderRequestSkuDTO::getFreightTemplateId));
        List<FreightCalcModel.FreightAndWeight> faws = new LinkedList<>();
        calcMap.forEach((k, v) -> {
            FreightCalcModel.FreightAndWeight faw = new FreightCalcModel.FreightAndWeight();
            faw.setId(k);
            int weight = 0;
            int price = 0;
            for (OrderRequestSkuDTO skuDTO : v) {
                weight += skuDTO.getWeight() * skuDTO.getNum();
                price += (user.getLevel() == UserLevelType.VIP.getCode() ? skuDTO.getVipPrice() : skuDTO.getPrice()) * skuDTO.getNum();
            }
            faw.setWeight(weight);
            faw.setPrice(price);
            faws.add(faw);
        });
        calcModel.setFreightAndWeights(faws);
        int sum = freightTemplateBizService.computePostage(calcModel);
        return sum;
    }

    @Override
    public List<Map<String,Object>> getInCook(Long zzjgId) {
        try {
            List<Map<String, Object>> list = null;
            Object timeExp = cacheComponent.getObj(CacheConst.IN_COOK_ORDER + zzjgId+"time",Object.class);
            if (timeExp == null) { //如果失效
                if (lockComponent.tryLock(LockConst.IN_COOK_LOCK + zzjgId, 20)) {
                    List<Map<String, Object>> newList = new ArrayList<>();
                    if (list == null) {
                        newList = orderMapper.countOrderInCook(zzjgId);
                        cacheComponent.putObj(CacheConst.IN_COOK_ORDER + zzjgId, newList);
                        cacheComponent.putObj(CacheConst.IN_COOK_ORDER + zzjgId+"time", newList,5*60);
                        return newList;
                    }
                }
            }
            //如果缓存不仅过期并且当前线程也没有拿到锁更新缓存那么就直接返回旧的缓存 ，旧的缓存是做了冗余只会逻辑过期
            list = cacheComponent.getObj(CacheConst.IN_COOK_ORDER + zzjgId, List.class);
            return list;
        }finally {//释放锁
            lockComponent.release(LockConst.IN_COOK_LOCK + zzjgId);
        }
    }

    @Override
    public List<Map<String, Object>> getOrderSku(String orderNo) {
        QueryWrapper<OrderSkuDO> wapper = new QueryWrapper<>();
        wapper.eq("order_no",orderNo);
        List<Map<String, Object>> orderSkuDOMaps = orderSkuMapper.selectMaps(wapper);
        return orderSkuDOMaps;
    }


    /**
     * 保存订单抽取接口
     *
     * @param skuOriginalPrice
     * @param skuPrice
     * @param channel
     * @param freightPrice
     * @param couponUserDTO
     * @param orderRequest
     * @param parentOrderNo
     * @param childIndex
     * @param userId
     * @param now
     * @param addressDO
     * @param skuDTOList
     * @param orderCalcSkuList
     * @param userLevel
     * @param activityType
     * @param activityId
     * @return
     * @throws ServiceException
     */
    private OrderDO save(int skuOriginalPrice, int skuPrice, String channel,
                         int freightPrice, CouponUserDTO couponUserDTO,
                         OrderRequestDTO orderRequest, String parentOrderNo, int childIndex,
                         Long userId, Date now, AddressDO addressDO, List<SkuDTO> skuDTOList,
                         List<OrderCalcSkuModel> orderCalcSkuList, Integer userLevel,
                         Integer activityType, Long activityId,Long zzjgId,String ipRegion) throws ServiceException {
        OrderDO orderDO = new OrderDO();
        // 设置商品原价，总价
        orderDO.setSkuOriginalTotalPrice(skuOriginalPrice);
        orderDO.setSkuTotalPrice(skuPrice);
        // 下单渠道
        orderDO.setChannel(channel);
        // 计算订单价格
        orderDO.setFreightPrice(freightPrice);
        // 计算订单优惠券价格
        int couponPrice = 0;
        if (couponUserDTO != null) {
            couponPrice = couponUserDTO.getDiscount();
            // 这里根据实际情况决定是否需要将邮费加入到优惠券计算中
            if (couponUserDTO.getMin() > skuPrice) {
                throw new AppServiceException(ExceptionDefinition.ORDER_COUPON_PRICE_NOT_ENOUGH);
            }
        }
        orderDO.setCouponPrice(couponPrice);
        orderDO.setActualPrice(skuPrice + freightPrice - couponPrice);
        if(couponUserDTO!=null){
        /**
         * 判断优惠卷类型 1 、满减卷  2、打折卷
         */
        Integer couponType = 1;
        couponType = couponUserDTO.getTypeShop();
        if(couponType==1){ //如果是满减卷
            orderDO.setActualPrice(skuPrice + freightPrice - couponPrice);
        }else if(couponType==2){//说明是折扣卷
            Double ddCouponZhekou = couponPrice/100/10.00;
            BigDecimal b = new BigDecimal(ddCouponZhekou);
            double dZK = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            Double actualPrice = (skuPrice + freightPrice) * dZK;
            orderDO.setActualPrice(actualPrice.intValue());   //设置优惠后的价格
            Double couponfinalPrice = (skuPrice + freightPrice) -actualPrice;
            orderDO.setCouponPrice(couponfinalPrice.intValue()); //设置新的优惠价格
        }else if(couponType == 3){ //免费卷
            orderDO.setActualPrice(1);
            orderDO.setCouponPrice(skuPrice + freightPrice-1);

        }
        }
        orderDO.setMono(orderRequest.getMono());
        orderDO.setParentOrderNo(parentOrderNo);
        orderDO.setOrderNo(parentOrderNo + "S" + (1000 + childIndex));
        orderDO.setUserId(userId);
        orderDO.setStatus(OrderStatusType.UNPAY.getCode());
        orderDO.setGmtUpdate(now);
        orderDO.setGmtCreate(now);
        orderDO.setZzjgId(zzjgId);
        orderDO.setPhone(orderRequest.getPhoneNum());
        try{
            if(!StringUtils.isEmpty(ipRegion)){
                String [] ips = ipRegion.split("\\|");
                if(ips.length>3){
                    String sheng = ips[1];
                    String shi = ips[2];
                    orderDO.setSheng(sheng);
                    orderDO.setShi(shi);
                }
            }
        }catch (Exception e){ //不做任何处理
            logger.info("解析ip出错：——"+ipRegion);
        }

        if(addressDO != null){ //如果为空则表示不需要邮寄
            if (!userId.equals(addressDO.getUserId())) {
                throw new AppServiceException(ExceptionDefinition.ORDER_ADDRESS_NOT_BELONGS_TO_YOU);
            }
            orderDO.setConsignee(addressDO.getConsignee());
            orderDO.setPhone(addressDO.getPhone());
            orderDO.setProvince(addressDO.getProvince());
            orderDO.setCity(addressDO.getCity());
            orderDO.setCounty(addressDO.getCounty());
            orderDO.setAddress(addressDO.getAddress());
        }
        // 冗余一个团购信息
        if (activityType != null && activityType == SpuActivityType.GROUP_SHOP.getCode()) {
            orderDO.setGroupShopId(activityId);
        }
        orderMapper.insert(orderDO);

        for (int i = 0; i < skuDTOList.size(); i++) {
            OrderCalcSkuModel orderCalcSpuDTO = orderCalcSkuList.get(i);
            SkuDTO skuDTO = skuDTOList.get(i);
            Assert.isTrue(orderCalcSpuDTO.getSkuId().longValue() == skuDTO.getId().longValue(), "断言失败！");
            OrderSkuDO orderSkuDO = new OrderSkuDO();
            orderSkuDO.setBarCode(skuDTO.getBarCode());
            orderSkuDO.setTitle(skuDTO.getTitle());
            orderSkuDO.setUnit(skuDTO.getUnit());
            orderSkuDO.setSpuTitle(skuDTO.getSpuTitle());
            orderSkuDO.setImg(skuDTO.getImg() == null ? skuDTO.getSpuImg() : skuDTO.getImg());
            orderSkuDO.setNum(orderCalcSpuDTO.getNum());
            orderSkuDO.setOriginalPrice(skuDTO.getOriginalPrice());
            orderSkuDO.setPrice(skuDTO.getPrice());
            if (userLevel == UserLevelType.VIP.getCode()) {
                orderSkuDO.setPrice(skuDTO.getVipPrice());
            } else {
                orderSkuDO.setPrice(skuDTO.getPrice());
            }
            orderSkuDO.setSkuId(skuDTO.getId());
            orderSkuDO.setSpuId(skuDTO.getSpuId());
            orderSkuDO.setOrderNo(orderDO.getOrderNo());
            orderSkuDO.setOrderId(orderDO.getId());
            orderSkuDO.setGmtCreate(now);
            orderSkuDO.setGmtUpdate(now);
            orderSkuDO.setSpecification(skuDTO.getSpecification());
            // 考虑到有的活动又不需要拆单，所以这里活动就只能关联到订单商品。也就是商品参加促销活动，而非订单参加。
            orderSkuDO.setActivityType(activityType);
            orderSkuDO.setActivityId(activityId);
            orderSkuMapper.insert(orderSkuDO);
        }
        return orderDO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object downWxBill(Long zzjgId,String date) throws ServiceException {

        String appId = unimallWxAppProperties.getMiniAppId();
        SysZzjg sysZzjg = sysZzjgMapper.selectByPrimaryKey(zzjgId);
        Long brandId = sysZzjg.getBrandId();
        if(brandId!=null){
            BrandDo brandDo = brandMapper.selectOne(new QueryWrapper<BrandDo>().eq("id",brandId));
            appId = brandDo.getMiniAppId();
        }
        try {
            WxPayDownloadBillRequest wxPayDownloadBillRequest = new WxPayDownloadBillRequest();
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            //            // 设置微信请求基本信息
            wxPayDownloadBillRequest.setAppid(appId);

            wxPayDownloadBillRequest.setSubMchId(sysZzjg.getMchId());


            /**
             * 还有一种可能 该服务商关联的主题小程序是其它小程序时做一些操作
             */
            utilServices.doSpecISPDownBill(sysZzjg,wxPayDownloadBillRequest);

            wxPayDownloadBillRequest.setBillDate(date);
            wxPayDownloadBillRequest.setBillType("ALL");
            wxPayDownloadBillRequest.setTarType("GZIP");
            WxPayService otherWxPayService =  utilServices.getOtherISPService(sysZzjg);
            WxPayBillResult wxPayBillResult = null;
            if(otherWxPayService !=null){
                wxPayBillResult =  otherWxPayService.downloadBill(wxPayDownloadBillRequest);
            }else{
               wxPayBillResult = wxPayService.downloadBill(wxPayDownloadBillRequest);
            }
            return wxPayBillResult;
        } catch (WxPayException e) {
            logger.error("[微信支付] 异常", e);
            throw new AppServiceException(e.getErrCodeDes(), ExceptionDefinition.THIRD_PART_SERVICE_EXCEPTION.getCode(), RepStatusCode.BUSINESS_ERROR);
        } catch (Exception e) {
            logger.error("[预付款异常]", e);
            throw new AppServiceException(ExceptionDefinition.ORDER_UNKNOWN_EXCEPTION);
        }
    }



}
